# -*- coding: utf-8 -*-

import sys
import shutil
import os
import gettext
import datetime

from kivy.app import App
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.utils import platform
from kivy.logger import Logger

import model
from model.gamestate import Phase
from model.dictionary import Dictionary
from model.dictionaryinfo import DictionaryInfo

from ui.welcome_screen import WelcomeScreen
from ui.add_player import AddPlayerScreen
from ui.add_words import AddWords
from ui.players_order import PlayersOrderWidget
from ui.from_to import FromToWidget
from ui.start_game import StartGame
from ui.guess_word import GuessWordScreen
from ui.presenter import Presenter
from ui.results_of_one_pair import ResultsOfOnePair
from ui.game_config import GameConfig
from ui.game_results import GameResults
from ui.countdown_screen import CountdownScreen
from ui.quick_game_settings import QuickGameSettings
from ui.rules import RulesScreen
from ui.cur_result import CurResults
from ui.load_game import LoadGameScreen
from ui.edit_players import EditPlayers
from ui.join import Join
from ui.about import AboutScreen
from ui.next_round import NextRound
from ui.hat_widget import open_yes_no_popup

from constants import PATH, LOCALE_PATH, CONFIG_FILE, CONFIG_TEMPLATE_FILE,\
    ICON, LOCALE, PICKLED_GAMESTATE, STATISTICS_FOLDER, KEYS_BACK, KEYS_HOME,\
    POPUP_TYPE


gettext.bindtextdomain('thehat', LOCALE_PATH)


def update_config():
    """ Checks the config file's version and updates it from template

        Checks the config file's version in the first line and, if it is out
        of date, replaces the config with the config template
    """
    with open(CONFIG_FILE, 'r') as config:
        conf_ver = config.readline()
        Logger.debug('config version is %s' % (conf_ver))
    with open(CONFIG_TEMPLATE_FILE, 'r') as template:
        temp_ver = template.readline()
        Logger.debug('config template version is %s' % (temp_ver))
    if conf_ver < temp_ver:
        shutil.copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE)
        Logger.debug('config is out of date. replacing it with template')


class MainView(Widget):
    pass


class MainViewApp(App):
    title = 'The Hat'
    """ Application title
    """

    icon = ICON
    """ Path to application icon
    """

    is_settings_open = False
    """ Indicates if settings screen is opened
    """

    def build(self):
        """ Creates app window

            Creates main_view widget, presenter widget, settings, sets screen
            orientation and binds keyboard listening
        """
        self.config = None
        self.create_config()
        locale = self.config.getdefault('Language', 'lang', LOCALE)
        self.set_locale(locale)
        self.main_view = MainView()
        self.presenter = Presenter(self.main_view, PATH)
        self.register_views()

        self.use_kivy_settings = False
        self.create_game_state_settings()

        self.curr_orientation = Window.rotation
        self.prev_orientation = Window.rotation

        self.main_view.bind(on_keyboard=self.handle_keyboard)
        self.update_confirmation_button_time()
        self.presenter.change_phase(Phase.WELCOME)
        return self.main_view

    def update_confirmation_button_time(self):
        from ui.confirmation_button import ConfirmationButton
        ConfirmationButton.time =\
            self.presenter.settings.game.confirmation_time_sec

    def set_locale(self, locale):
        try:
            lang = gettext.translation(domain='thehat', localedir=LOCALE_PATH,
                                       languages=[locale], codeset='utf-8')
            lang.install(True)
            Logger.debug('main.py: set_locale(): locale ' + locale +
                         ' sucessfully installed')
        except:
            Logger.warn('main.py: set_locale(): unknown locale %s'
                        % locale)
            Logger.warn('main.py: set_locale(): using default locale %s'
                        % LOCALE)
            self.set_locale(LOCALE)

    def register_views(self):
        """ Adds all screens to presenter
        """
        views = [
            (Phase.WELCOME, WelcomeScreen, ['welcome_screen']),
            (Phase.START_GAME, StartGame, ['start_game']),
            (Phase.GUESS_WORD, GuessWordScreen, ['guesswordscreen']),
            (Phase.ADD_PLAYER, AddPlayerScreen, ['add_player']),
            (Phase.ADD_WORDS, AddWords, ['add_words']),
            (Phase.FROM_TO, FromToWidget, ['from_to']),
            (Phase.PLAYERS_ORDER, PlayersOrderWidget, ['players_order']),
            (Phase.RESULTS_OF_ONE_PAIR, ResultsOfOnePair,
                ['results_of_one_pair']),
            (Phase.GAME_CONFIG, GameConfig, ['gameconfig']),
            (Phase.COUNTDOWN, CountdownScreen, ['countdown_screen']),
            (Phase.GAME_RESULTS, GameResults, ['gameresults']),
            (Phase.QUICK_GAME_SETTINGS, QuickGameSettings,
                ['quick_game_settings']),
            (Phase.RULES, RulesScreen, ['rules']),
            (Phase.CURRES, CurResults, ['curresults']),
            (Phase.LOAD_GAME, LoadGameScreen, ['loadgame']),
            (Phase.JOIN, Join, ['join']),
            (Phase.EDIT_PLAYERS, EditPlayers, ['edit_players']),
            (Phase.ABOUT, AboutScreen, ['about']),
            (Phase.NEXT_ROUND, NextRound, ['nextround'])]

        for view in views:
            self.presenter.register_view(*view)

    def create_game_state_settings(self):
        """ Creates rpesenter.game_state if they do not exist
        """
        if (not hasattr(self.presenter.game_state, 'settings') or
                self.presenter.game_state.settings is None):
            self.presenter.game_state.settings = model.gamestate.Settings()

    def create_config(self):
        """ Loads and parses configuration file
        """
        if self.config is None:
            self.load_config()
            Logger.debug('Loading config from '+CONFIG_FILE)
            self.config.read(CONFIG_FILE)
            Logger.debug('Updating config with dictionary data')
            metadata = DictionaryInfo.parse_dictionaries_metadata()
            for dictinfo in metadata:
                self.config.setdefault(
                    'Dictionaries Defaults',
                    dictinfo.file_name,
                    1 if dictinfo.enable_by_default else 0)

    def _create_settings(self):
        from ui.app_settings import AppSettings
        if self._app_settings is None:
            self._app_settings = s = AppSettings()
            s.bind(on_close=self.close_settings,
                   on_config_change=self._on_config_change)
            s.phase = self.presenter.game_state.phase
        return self._app_settings

    def build_settings_menu(self):
        """ Assembles settings screen from settings panels
        """
        curr_phase = self.presenter.game_state.phase
        settings = self._app_settings

        if curr_phase in [Phase.GUESS_WORD, Phase.COUNTDOWN]:
            return False
        if settings is not None and hasattr(settings, 'phase') and\
           settings.phase == curr_phase:
            self._app_settings = settings
            return True

        self._app_settings = None
        self._create_settings()
        settings = self._app_settings

        panels = []
        panels.append((_('Interface settings'), PATH+'/data/gameconfig.json'))
        panels.append((_('Sound settings'), PATH+'/data/soundconfig.json'))

        select_dictionaries = False
        if curr_phase == Phase.WELCOME:
            panels.append((_('Quick game settings'),
                          PATH+'/data/defquickgameconfig.json'))
            panels.append((_('Game settings'),
                          PATH+'/data/defgameconfig.json'))
            select_dictionaries = True

        for panel in panels:
            settings.add_json_panel(panel[0], self.config, panel[1])

        if select_dictionaries:
            settings.add_json_panel(_('Dictionaries settings'), self.config,
                                    data=str(Dictionary.make_json()))

        self._app_settings = settings
        return True

    def rotate_horizontally(self):
        """ Changes window rotation to horizontal
        """
        horizontal_orientation =\
            self.presenter.settings.graphics.horizontal_orientation
        self.presenter.set_orientation(horizontal_orientation)

    def rotate_vertically(self):
        """ Changes window rotation to vertical
        """
        vertical_orientation =\
            self.presenter.settings.graphics.vertical_orientation
        self.presenter.set_orientation(vertical_orientation)

    def open_settings(self, *largs):
        """ Called when settings button is pressed and settings are to open
        """
        self.rotate_horizontally()
        self.is_settings_open = self.build_settings_menu() and\
            super(MainViewApp, self).open_settings(*largs)
        return self.is_settings_open

    def close_settings(self, *largs):
        """ Called when settings button is pressed and settings are to close
        """
        # copypaste from kivy/app.py
        win = self._app_window
        settings = self._app_settings
        if win is None or settings is None:
            return
        if settings in win.children and settings.on_close() in [None, True]:
            win.remove_widget(settings)
            self.is_settings_open = False

            self.presenter.rotate()
            self.presenter.update()
            return True
        return False

    def reload_screen(self):
        self.presenter.change_phase(self.presenter.game_state.phase)
        self.rotate_horizontally()

    def on_config_change(self, config, section, key, value):
        """ Called when settings change on settings screen
        """
        self.config.write()
        attr = (section, key)

        if attr == ('Language', 'lang'):
            Logger.debug('main.py: on_config_change(): set lang to %s'
                         % value)

            self.set_locale(value)
            self.reload_screen()
            return

        self.presenter.settings.set_property(section, key, value)

        if section == 'Dictionaries Defaults':
            self.reload_screen()

        if section == 'Graphics':
            orientation =\
                self.presenter.settings.graphics.horizontal_orientation
            self.presenter.set_orientation(orientation)

        self.update_confirmation_button_time()
        self.reload_screen()

    def on_pause(self):
        """ Called when application is going to be paused
        """
        return self.presenter.close_app()

    def on_stop(self):
        """ Called when application is going to be closed
        """
        if hasattr(self, 'presenter'):
            return self.presenter.close_app()

    def _back(self):
        popups_closed = False

        for child in self.main_view.parent.children:
            if isinstance(child, POPUP_TYPE):
                Logger.debug('Dismissing popup.')
                child.dismiss()
                popups_closed = True

        if popups_closed:
            return True

        if self.is_settings_open:
            Logger.debug('Closing settings.')
            self.close_settings()
            return True

        if self.presenter.game_state.phase == Phase.WELCOME:
            Logger.debug('Prompt on pause.')
            open_yes_no_popup(self.main_view, _('Notification'),
                              _('Do you really want to exit?'),
                              yes_action=self.stop, no_action=None)
            return True

        Logger.debug('Heading to previous screen.')
        self.main_view.content.children[0].go_back()
        return True

    def _home(self):
        Logger.debug('Pausing.')
        self.on_pause()
        return True

    def handle_keyboard(self, window, key, *largs):
        """ Handles keyboard
        """
        Logger.debug('Key %s was pressed.' % key)
        if self.presenter.game_state.phase == Phase.GUESS_WORD:
            pass

        if key in KEYS_BACK:
            return self._back()
        if key in KEYS_HOME:
            return self._home()

    def on_start(self):
        """ Called when application is going to be resumed

            Remaps hardware keys and resumes application
        """
        if platform() == 'android':
            import android

            android.map_key(android.KEYCODE_BACK, 1001)
            android.map_key(android.KEYCODE_HOME, 1002)

        self._app_window.bind(on_keyboard=self.handle_keyboard)
        self.presenter.resume_app()


def run_app():
    if not os.path.exists(CONFIG_FILE):
        Logger.warn('No config found')
        if not os.path.exists(CONFIG_TEMPLATE_FILE):
            raise Exception('No config files found')
        else:
            shutil.copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE)

    if os.path.exists(CONFIG_FILE) and\
            os.path.exists(CONFIG_TEMPLATE_FILE):
        update_config()

    if not os.path.exists(STATISTICS_FOLDER):
        os.mkdir(STATISTICS_FOLDER)
    start_time = datetime.datetime.now()
    main_view_app = MainViewApp()
    if 'nocatch' in sys.argv:
        main_view_app.run()
    else:
        try:
            main_view_app.run()
        except Exception as msg:
            main_view_app.on_stop()

            Logger.debug('Moving files after crash')

            now = datetime.datetime.now()
            time_stamp = now.strftime('%m.%d_%H.%M.%S')

            if os.path.exists(PICKLED_GAMESTATE):
                shutil.copyfile(PICKLED_GAMESTATE,
                                PICKLED_GAMESTATE+'.crash.'+time_stamp)

            if (now - start_time).total_seconds() < 10:
                Logger.error('Crash within 10 seconds. Removing files.')
                if os.path.exists(PICKLED_GAMESTATE):
                    os.remove(PICKLED_GAMESTATE)
                if os.path.exists(CONFIG_FILE):
                    os.remove(CONFIG_FILE)
            import traceback
            traceback.print_exc()
            raise msg


if __name__ == "__main__":
    run_app()
